#pragma once

#include <iostream>
#include <string>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>

#define CONV(addrptr) (struct sockaddr*)addrptr

enum{
    Socket_err = 1,
    Bind_err,
    Listen_err
};

const static int defalutsockfd = -1;
const int defalutbacklog = 5;

class Socket
{
public:
    virtual ~Socket(){};
    virtual void CreateSocketOrDie() = 0;
    virtual void BindSocketOrDie(uint16_t port) = 0;
    virtual void ListenSocketOrDie(int backlog) = 0;
    virtual Socket* AcceptConnection(std::string* ip , uint16_t* port) = 0;
    virtual bool ConnectServer(const std::string& serverip , uint16_t serverport) = 0;
    virtual int GetSockFd() = 0;
    virtual void SetSockFd(int sockfd) = 0;
    virtual void CloseSockFd() = 0;
    virtual bool Recv(std::string& buffer,int size) = 0;
    virtual void Send(const std::string& send_string) = 0;

public:
    void BuildListenSocketMethod(uint16_t port)
    {
        CreateSocketOrDie();
        BindSocketOrDie(port);
        ListenSocketOrDie(defalutbacklog);
    }
    bool BuildConnectSocketMethod(const std::string& serverip , uint16_t serverport)
    {
        CreateSocketOrDie();
        return ConnectServer(serverip,serverport);
    }
    void BuildNormalSocketMethod(int sockfd)
    {
        SetSockFd(sockfd);
    }
};

class TcpSocket : public Socket
{
public:
    TcpSocket(int sockfd = defalutsockfd)
        :_sockfd(sockfd)
    {}
    ~TcpSocket(){};

    void CreateSocketOrDie() override
    {
        _sockfd = ::socket(AF_INET,SOCK_STREAM,0);
        if(_sockfd < 0) exit(Socket_err);
    }

    void BindSocketOrDie(uint16_t port) override
    {
        struct sockaddr_in addr;
        memset(&addr,0,sizeof(addr));

        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = INADDR_ANY;
        addr.sin_port = htons(port);
        socklen_t len = sizeof(addr);

        int n = ::bind(_sockfd,CONV(&addr),len);
        if(n < 0) exit(Bind_err);
    }

    void ListenSocketOrDie(int backlog) override
    {
        int n = ::listen(_sockfd,backlog);
        if(n < 0) exit(Listen_err);
    }

    Socket* AcceptConnection(std::string* clientip , uint16_t* clientport) override
    {
        struct sockaddr_in client;
        memset(&client,0,sizeof(client));
        socklen_t len = sizeof(client);
        int fd = ::accept(_sockfd,CONV(&client),&len);

        if(fd < 0) return nullptr;

        char buffer[64];
        inet_ntop(AF_INET,&client.sin_addr,buffer,len);
        *clientip = buffer;
        *clientport = ntohs(client.sin_port);

        Socket* s = new TcpSocket(fd);

        return s;
    }   

    bool ConnectServer(const std::string& serverip , uint16_t serverport) override
    {
        struct sockaddr_in server;
        memset(&server,0,sizeof(server));
        server.sin_family = AF_INET;
        // server.sin_addr.s_addr =  inet_addr(serverip.c_str());
        inet_pton(AF_INET,serverip.c_str(),&server.sin_addr);
        server.sin_port = htons(serverport);
        socklen_t len = sizeof(server);
        
        int n = connect(_sockfd,CONV(&server),len);
        if(n < 0) return false;
        else return true;
    }
    
    int GetSockFd() override
    {
        return _sockfd;
    }
    void SetSockFd(int sockfd) override
    {
        _sockfd = sockfd;
    }

    void CloseSockFd() override
    {
        if(_sockfd > defalutsockfd)
        {
            close(_sockfd);
        }
    }

    bool Recv(std::string& buffer , int size)override
    {
        char inbuffer[size];
        int n = recv(_sockfd,inbuffer,sizeof(inbuffer)-1,0);
        if(n > 0)
        {
            inbuffer[n] = 0;
        }
        else
        {
            return false;
        }

        buffer += inbuffer;

        return true;
    }

    void Send(const std::string& send_string)
    {
        send(_sockfd,send_string.c_str(),send_string.size(),0);
    }


private:
    int _sockfd;
};